;
; Lab2.asm
;
; Author: Evan Zimmerman <zimmermane@msoe.edu>
; Date: 9/17/12
; Course: CE4920-001
; Instructor: Barnicki
;
; Purpose: Simulates a basic operating system by switching between
;          four processes which output their process number to
;          PORTC. Processes are context switched every 20ms by
;          the Timer1 output compare match interrupt routine.
; 


.include "m32def.inc"

                        ; Kernel Variables
.equ SPHP0 = 0x0060     ; SPH P0
.equ SPLP0 = 0x0061     ; SPL P0
.equ SPHP1 = 0x0062     ; SPH P1
.equ SPLP1 = 0x0063     ; SPL P1
.equ SPHP2 = 0x0064     ; SPH P2
.equ SPLP2 = 0x0065     ; SPL P2
.equ SPHP3 = 0x0066     ; SPH P3
.equ SPLP3 = 0x0067     ; SPL P3
.equ pcounter = 0x0068  ; process counter
.equ tempreg = 0x0069   ; temporary location for holding a register when pushing/popping

.dseg         ; data segment: user RAM
.org 0x0060   ; start of data memory
	.byte 10  ; kernel variables reserved here

.cseg         ; program segment: flash ROM
.org 0x0000   ;vector table address (ref guide page 4)
	rjmp INIT ;reset vector

.org 0x000E      ;vector table address (ref guide page 4)
	rjmp OC1AISR ;OCR1A ISR vector jump

.org 0x002A   ;first address in program memory

INIT:  ; initialize the system upon reset

	;set up the process control blocks for each process
	ldi r16,0x01   ;P0 at 0x017F
	sts SPHP0,r16
	ldi r16,0x7F
	sts SPLP0,r16
	ldi r16,0x01   ;P1 at 0x01FF
	sts SPHP1,r16
	ldi r16,0xFF
	sts SPLP1,r16  
	ldi r16,0x02   ;P2 at 0x027F
	sts SPHP2,r16
	ldi r16,0x7F
	sts SPLP2,r16
	ldi r16,0x02   ;P3 at 0x02FF
	sts SPHP3,r16
	ldi r16,0xFF
	sts SPLP3,r16
	
	;initialize process counter to 0 (Process 0)
	ldi r16,0x00
	sts pcounter,r16

	;initialize the process stacks:
	; - push the process' address to its stack
	; - push the process' registers to its stack (initially all zero)
	; - save stack pointer

	;set stack pointer to Process 0
	lds r16,SPHP0 
	out sph,r16
	lds r16,SPLP0
	out spl,r16

	ldi r16,low(P0)  ;Push P0's address
	push r16
	ldi r16,high(P0)
	push r16
	ldi r16,0x00     
	push r16		 ;Push P0's SREG
	push r16         ;push all 32 registers (initially 0)
	push r16         
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16

	lds r16,SPHP1    ;set stack pointer to P1
	out sph,r16
	lds r16,SPLP1
	out spl,r16

	ldi r16,low(P1)  ;Push P1's address
	push r16
	ldi r16,high(P1)
	push r16
	ldi r16,0x00     
	push r16         ;Push P1's SREG
	push r16         ;push all 32 registers (initially 0)
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16

	in r16,sph     ;store P1's stack pointer
	sts SPHP1,r16
	in r16,spl
	sts SPLP1,r16

	lds r16,SPHP2   ;set stack pointer to P2
	out sph,r16
	lds r16,SPLP2
	out spl,r16

	ldi r16,low(P2)  ;Push P2's address
	push r16
	ldi r16,high(P2)
	push r16
	ldi r16,0x00
	push r16         ;Push P2's SREG
	push r16         ;push all 32 registers (initially 0)
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16

	in r16,sph     ;store P2's stack pointer
	sts SPHP2,r16
	in r16,spl
	sts SPLP2,r16

	lds r16,SPHP3  ;set stack pointer to P3
	out sph,r16
	lds r16,SPLP3
	out spl,r16

	ldi r16,low(P3)  ;Push P3's address
	push r16
	ldi r16,high(P3)
	push r16
	ldi r16,0x00
	push r16         ;Push P2's SREG
	push r16         ;push all 32 registers (initially 0)
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16
	push r16

	in r16,sph       ;store P3's stack pointer
	sts SPHP3,r16
	in r16,spl
	sts SPLP3,r16

	;initialize the Timer1 interrupt for a 20ms 
	;context switch between processes
	;16MHz clock with prescaler of 256 = 62.5KHz
	;1/62.5KHz = 16us/tick
	;20ms/16us = 1250 ticks (0x04E2) (OC1A value)
	ldi r16,0x04  
	out OCR1AH,r16       ;load 0x04E2 as compare value
	ldi r16,0xE2
	out OCR1AL,r16

	in r16,TIMSK         ;enable OCR1A compare match interrupt
	ori r16,(1<<OCIE1A)
	out TIMSK,r16

	ldi r16,0x00         ;start counting at 0
	out TCNT1H,r16
	out TCNT1L,r16

	in r16,TCCR1B        ; set timer 1 for CTC mode with prescaler of 256
	ori r16,(1<<WGM12)|(1<<CS12)
	out TCCR1B,r16

	sei ;enable interrupts

	lds r16,SPHP0   ;set stack pointer to p0
	out sph,r16
	lds r16,SPLP0
	out spl,r116

	ldi r16,0xFF
	out DDRC,r16    ;set up DDRC as all output

	rjmp P0         ;begin the program with P0


;Timer1 Output Compare interrupt vector routine 
;for context switching every 20ms
OC1AISR:
	sts tempreg,r0 ;temporarily save r0 to read in the SREG
    in r0,sreg     ;get/save the SREG
	push r0
	lds r0,tempreg ;restore r0
	push r0        ;push all registers
	push r1
	push r2
	push r3
	push r4
	push r5
	push r6
	push r7
	push r8
	push r9
	push r10
	push r11
	push r12
	push r13
	push r14
	push r15
	push r16
	push r17
	push r18
	push r19
	push r20
	push r21
	push r22
	push r23
	push r24
	push r25
	push r26
	push r27
	push r28
	push r29
	push r30
	push r31

	;increment process counter to determine which process
	;to switch to
	lds r16,pcounter
	inc r16          
	sts pcounter,r16
	cpi r16,0
	breq switchToP0
	cpi r16,1
	breq switchToP1
	cpi r16,2
	breq switchToP2
	cpi r16,3
	breq switchToP3

	;store P3's stack pointer and load P0's stack pointer
	switchToP0:
		in r16,sph
		sts SPHP3,r16
		in r16,spl
		sts SPLP3,r16
		lds r16,SPHP0
		out sph,r16
		lds r16,SPLP0
		out spl,r16
		rjmp start
	;store P0's stack pointer and load P1's stack pointer
	switchToP1:
		in r16,sph
		sts SPHP0,r16
		in r16,spl
		sts SPLP0,r16
		lds r16,SPHP1
		out sph,r16
		lds r16,SPLP1
		out spl,r16
		rjmp start
    ;store P1's stack pointer and load P2's stack pointer
	switchToP2:
		in r16,sph
		sts SPHP1,r16
		in r16,spl
		sts SPLP1,r16
		lds r16,SPHP2
		out sph,r16
		lds r16,SPLP2
		out spl,r16
		rjmp start
    ;store P2's stack pointer and load P3's stack pointer
	;also set pcounter to 0xFF so on the next switch it will
	;roll over back to process 0
	switchToP3:
		lds r16,pcounter
		ldi r16,0xFF
		sts pcounter,r16
		in r16,sph
		sts SPHP2,r16
		in r16,spl
		sts SPLP2,r16
		lds r16,SPHP3
		out sph,r16
		lds r16,SPLP3
		out spl,r16
		rjmp start

	;restores all registers for the process by popping
	;them off the stack in the reverse order
	start:
		pop r31 
		pop r30
		pop r29
		pop r28
		pop r27
		pop r26
		pop r25
		pop r24
		pop r23
		pop r22
		pop r21
		pop r20
		pop r19
		pop r18
		pop r17
		pop r16
		pop r15
		pop r14
		pop r13
		pop r12
		pop r11
		pop r10
		pop r9
		pop r8
		pop r7
		pop r6
		pop r5
		pop r4
		pop r3
		pop r2 
		pop r1
		pop r0
		sts tempreg,r0 ;store r0 so we can also restore the SREG
		pop r0         
		out sreg,r0    ;restore the SREG
		lds r0,tempreg ;restore r0
	reti ;return from the interrupt


P0:  ;Process 0
	ldi r16,0x00
	out PORTC,r16  ;output process number to PORTC
	rjmp P0

P1:  ;Process 1
	ldi r16,0x01
	out PORTC,r16  ;output process number to PORTC
	rjmp P1

P2:  ;Process 2
	ldi r16,0x02
	out PORTC,r16  ;output process number to PORTC
	rjmp P2

P3:  ;Process 3
	ldi r16,0x03
	out PORTC,r16  ;output process number to PORTC
	rjmp P3